<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FBA物流追踪</title>
    <!-- 引入Element UI样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入SheetJS库用于处理Excel文件 -->
    <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
    <!-- 引入Vue -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <!-- 引入Element UI -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <style>
        /* 页面基础样式 */
        body {
            font-family: Arial, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        .container {
            margin-bottom: 30px;
        }
        h1 {
            color: #333;
            text-align: center;
        }
        /* 输入区域样式 */
        .input-area {
            border: 2px dashed #ccc;
            border-radius: 8px;
            padding: 20px;
            margin-bottom: 20px;
            background-color: #f9f9f9;
        }
        .input-area textarea {
            width: 100%;
            min-height: 100px;
            margin-bottom: 10px;
            padding: 10px;
            box-sizing: border-box;
        }
        /* 表格容器样式 */
        .table-container {
            margin-top: 20px;
            border: 1px solid #ddd;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        /* 头部控制区域样式 */
        .header-controls {
            margin: 15px 0;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
        }
        /* 结果信息样式 */
        .result-info {
            margin: 10px 0;
            color: #666;
            font-style: italic;
        }
        /* 无结果提示样式 */
        .no-results {
            text-align: center;
            color: #999;
            padding: 20px;
            font-style: italic;
        }
        /* 高亮样式 */
        .highlight {
            background-color: #FFFF00;
            font-weight: bold;
        }
        /* 强制不换行 */
        .el-table th, .el-table td {
            white-space: nowrap !important;
            overflow: hidden;
        }
        .el-table th > .cell, .el-table td > .cell {
            white-space: nowrap !important;
            word-break: keep-all;
        }
        /* 物流信息弹窗样式 */
        .logistics-dialog .el-dialog__body {
            padding: 20px;
        }
        .logistics-title {
            font-size: 16px;
            font-weight: bold;
            margin-bottom: 15px;
            color: #333;
        }
        .logistics-info {
            margin-bottom: 10px;
            padding: 8px;
            background-color: #f5f7fa;
            border-radius: 4px;
        }
        .logistics-info span {
            font-weight: bold;
            margin-right: 10px;
        }
        .timeline-container {
            max-height: 400px;
            overflow-y: auto;
            padding-right: 10px;
        }
        /* 操作按钮样式 */
        .operation-btn {
            padding: 5px 10px;
            font-size: 12px;
        }
         /* 批量同步按钮样式 */
         .batch-sync-btn {
             margin-left: 10px;
         }
        /* 添加用于行着色的CSS类 (提高优先级) */
        .el-table .fba-group-0 {
            background-color: #fff5f5 !important; /* 浅红色 */
        }
        .el-table .fba-group-1 {
            background-color: #f0fff4 !important; /* 浅绿色 */
        }
        .el-table .fba-group-2 {
            background-color: #f0f6ff !important; /* 浅蓝色 */
        }
        .el-table .fba-group-3 {
            background-color: #fff9db !important; /* 浅黄色 */
        }
        .el-table .fba-group-4 {
            background-color: #f8f0ff !important; /* 浅紫色 */
        }
        .el-table .fba-group-5 {
            background-color: #ffe8f0 !important; /* 浅粉色 */
        }
        .el-table .fba-group-6 {
            background-color: #e6f7ff !important; /* 浅青色 */
        }
        .el-table .fba-group-7 {
            background-color: #fff0e6 !important; /* 浅橙色 */
        }
        .el-table .fba-group-8 {
            background-color: #f5f5f5 !important; /* 浅灰色 */
        }
        .el-table .fba-group-9 {
            background-color: #eafff5 !important; /* 浅薄荷绿 */
        }
        /* 可以根据需要添加更多颜色 */
    </style>
</head>
<body>
    <div id="app">
        <div class="container">
            <h1>FBA物流追踪</h1>
            <!-- 输入区域 -->
            <div class="input-area">
                <p>请输入FBA货件号，每行一个，或用逗号、空格分隔：</p>
                <textarea v-model="inputFbas" placeholder="例如: FBA1234567890&#10;FBA0987654321&#10;FBA1122334455"></textarea>
                <el-button type="primary" @click="startQuery" :disabled="isQuerying">
                    {{ isQuerying ? '查询中...' : '开始查询' }}
                </el-button>
                <el-button type="danger" @click="clearHistory" style="margin-left: 10px;">
                    清空历史
                </el-button>
            </div>

            <!-- 查询和控制区域 -->
            <div>
                <!-- 结果信息显示 -->
                <div class="result-info">{{ resultInfo }}</div>
            </div>
            <!-- 分页控制区域 -->
            <div class="header-controls">
                <div>
                  
                   
                    <!-- 导出 Excel 按钮 -->
                    <el-button type="success" @click="exportToExcel" :disabled="queryResults.length === 0">
                        导出 Excel
                    </el-button>
                </div>
                <div v-if="!showAll && queryResults.length > 0">
                    <span>
                        第 {{ currentPage }} 页，共 {{ totalPages }} 页
                        (显示 {{ startIndex + 1 }}-{{ endIndex }} 条)
                    </span>
                    <el-button
                        :disabled="currentPage === 1"
                        @click="previousPage">
                        上一页
                    </el-button>
                    <el-button
                        :disabled="currentPage === totalPages"
                        @click="nextPage">
                        下一页
                    </el-button>
                </div>
            </div>
            <!-- 表格显示区域 -->
            <div class="table-container" v-if="queryResults.length > 0">
                <el-table
                    :data="paginatedData"
                    highlight-current-row
                    size="mini"
                    :max-height="showAll ? 800 : 500"
                    border
                    @sort-change="handleSortChange"
                    ref="dataTable"
					stripe
                    >
                    <el-table-column
                        type="index"
                        width="50">
                    </el-table-column>
                    <!-- 固定列 -->
                    <el-table-column
                        prop="fbaNumber"
                        label="FBA货件号"
                        sortable
                        :min-width="getColumnWidth('fbaNumber')">
                    </el-table-column>
                    <el-table-column
                        prop="carrier"
                        label="货代"
                        sortable
                        :min-width="getColumnWidth('carrier')">
                    </el-table-column>
                    <el-table-column
                        prop="shippingTime"
                        label="发货时间"
                        sortable
                        :min-width="getColumnWidth('shippingTime')">
                    </el-table-column>
                    <el-table-column
                        prop="departureTime"
                        label="开船时间"
                        sortable
                        :min-width="getColumnWidth('departureTime')">
                    </el-table-column>
                    <el-table-column
                        prop="deliveryTime"
                        label="签收时间"
                        sortable
                        :min-width="getColumnWidth('deliveryTime')">
                    </el-table-column>
                    <el-table-column
                        prop="duration"
                        label="开船-签收时长"
                        sortable
                        :min-width="getColumnWidth('duration')">
                    </el-table-column>
                    <el-table-column
                        prop="status"
                        label="状态"
                        sortable
                        :min-width="getColumnWidth('status')">
                    </el-table-column>
                    <!-- 操作列 -->
                    <el-table-column
                        label="操作"
                        width="100"
                        fixed="right">
                        <template slot-scope="scope">
                            <el-button
                                type="primary"
                                size="mini"
                                class="operation-btn"
                                @click="fetchLogisticsInfo(scope.row)"
                                :disabled="!scope.row.fbaNumber">
                                查询物流
                            </el-button>
                        </template>
                    </el-table-column>
                    <!-- 物流信息列 -->
                    <el-table-column
                        label="物流信息"
                        width="200"
                        fixed="right">
                        <template slot-scope="scope">
                            <div v-if="scope.row.logisticsInfo" class="logistics-preview">
                                <div v-if="scope.row.logisticsInfo.shipment && scope.row.logisticsInfo.shipment.status">
                                    状态: {{ getStatusText(scope.row.logisticsInfo.shipment.status) }}
                                </div>
                                <div v-if="scope.row.logisticsInfo.shipment && scope.row.logisticsInfo.shipment.traces && scope.row.logisticsInfo.shipment.traces.length > 0">
                                    最新: {{ scope.row.logisticsInfo.shipment.traces[0].info }}
                                </div>
                                <el-button
                                    type="text"
                                    size="mini"
                                    @click="showLogisticsDetail(scope.row)">
                                    查看详情
                                </el-button>
                            </div>
                            <div v-else>
                                <el-button
                                    type="text"
                                    size="mini"
                                    @click="fetchLogisticsInfo(scope.row)"
                                    :disabled="!scope.row.fbaNumber">
                                    点击查询
                                </el-button>
                            </div>
                        </template>
                    </el-table-column>
                </el-table>
                <!-- 大数据量提示 -->
                <div v-if="showAll && queryResults.length > 1000"
                     style="color: #999; font-size: 12px; text-align: center; padding: 10px;">
                    提示：数据量较大，滚动可能稍慢。
                </div>
            </div>
            <!-- 无结果提示 -->
            <div v-else class="no-results">
                暂无查询结果
            </div>
            <!-- 物流信息弹窗 -->
            <el-dialog
                title="物流信息详情"
                :visible.sync="logisticsDialogVisible"
                width="600px"
                class="logistics-dialog">
                <div v-if="currentLogisticsInfo" class="logistics-content">
                    <div class="logistics-title">基础信息</div>
                    <div class="logistics-info">
                        <span>货件号:</span> {{ currentLogisticsInfo.shipment.client_reference }}
                    </div>
                    <div class="logistics-info">
                        <span>外部单号:</span> {{ currentLogisticsInfo.shipment.ext_number }}
                    </div>
                    <div class="logistics-info">
                        <span>状态:</span> {{ getStatusText(currentLogisticsInfo.shipment.status) }}
                    </div>
                    <div class="logistics-info">
                        <span>国家:</span> {{ currentLogisticsInfo.shipment.country }}
                    </div>
                    <div class="logistics-info">
                        <span>邮编:</span> {{ currentLogisticsInfo.shipment.postcode }}
                    </div>
                    <div class="logistics-title" style="margin-top: 20px;">物流轨迹</div>
                    <div class="timeline-container">
                        <el-timeline>
                            <el-timeline-item
                                v-for="(trace, index) in currentLogisticsInfo.shipment.traces"
                                :key="index"
                                :timestamp="trace.time"
                                placement="top">
                                {{ trace.info }}
                            </el-timeline-item>
                        </el-timeline>
                    </div>
                </div>
            </el-dialog>
        </div>
    </div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    inputFbas: '', // 用户输入的FBA号
                    queryResults: [], // 存储查询结果的数组
                    isQuerying: false, // 是否正在查询
                    currentPage: 1,
                    rowsPerPage: 20,
                    currentSortColumn: null, // 'fbaNumber', 'carrier', 'shippingTime', etc.
                    sortDirection: 'asc',
                    columnWidths: {},
                    showAll: true,
                    // 物流信息相关
                    logisticsDialogVisible: false,
                    currentLogisticsInfo: null,
                    // 货代映射 (保持不变)
                    carrierMap: {
                        "皓鹏": {
                            url: "http://haopeng.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
                        "速鸟/南鹏": {
                            url: "http://47.107.103.136:8000/cms/tracking",
                            method: "POST",
                            params: ""
                        },
                        "凯鑫": {
                            url: "http://kxinex.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
                        "小满速达": {
                            url: "http://xmsdwl.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
						"炜易达/欧地达": {
                            url: "http://weiyda.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
						"鼎运": {
                            url: "http://szdygj.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
						"恒信": {
                            url: "http://szfxin.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
						"丰信": {
                            url: "http://hengxe.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
						"蓝图梦": {
                            url: "http://gdltkj.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
                        "百易": {
                            url: "http://baiyix.nextsls.com/tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
                        "炜易达/欧地达": {
                            url: "http://oddexp.nextsls.com//tracking/app",
                            method: "GET",
                            params: "inajax=1&tracking_number="
                        },
                        "乐丰": {
                            loginUrl: "http://106.52.82.87:9991/api/user/login",
                            getPageDataUrl: "http://106.52.82.87:9991/api/OrderBulkCargo/getPageData",
                            trackingUrl: "http://106.52.82.87:9991/api/OrderBulkCargo/GetOrderDetail",
                            method: "POST",
                            loginData: {
                                "userName": "SJMY",
                                "password": "SJMY123.",
                                "verificationCode": ""
                            }
                        },
                        "大森林": {
                            loginUrl: "https://do.do2do.com/api/sys/login",
                            getOrderPageUrl: "https://do.do2do.com/api/customer/clientOs/getClientTtOrderPage",
                            trackingUrl: "https://do.do2do.com/api/customer/clientOs/getClientStatusData",
                            method: "GET",
                            cusId: "1826524249607639041", // 固定的cusId
                            loginData: {
                                "serachValue": "广州市时佳贸易有限公司",
                                "password": "Zhimei123",
                                "type": "2"
                            }
                        }
                    },
                    // 状态映射 (保持不变)
                    statusMap: {
                        "ready": "已下单",
                        "picked": "已收货",
                        "in_transit": "转运中",
                        "delivered": "已签收",
                        "returned": "退件",
                        "cancelled": "已取消"
                    },
                    // 认证信息 (保持不变)
                    lefengToken: null,
                    lefengTokenExpiry: null,
                    dslToken: null,
                    dslTokenExpiry: null,
                    // IndexedDB 配置
                    dbName: 'FbaTrackingDB',
                    dbVersion: 1,
                    db: null,
                };
            },
            computed: {
                totalPages() {
                    return Math.max(1, Math.ceil(this.queryResults.length / this.rowsPerPage));
                },
                startIndex() {
                    return (this.currentPage - 1) * this.rowsPerPage;
                },
                endIndex() {
                    return Math.min(this.startIndex + this.rowsPerPage, this.queryResults.length);
                },
                paginatedData() {
                    const data = this.showAll
                        ? this.queryResults
                        : this.queryResults.slice(this.startIndex, this.startIndex + this.rowsPerPage);
                    return data;
                },
                resultInfo() {
                   return `共 ${this.queryResults.length} 条记录`;
                }
            },
            watch: {
                queryResults: {
                    handler() {
                        this.$nextTick(() => {
                            this.calculateColumnWidths();
                        });
                        // 保存到 IndexedDB
                        this.saveToIndexedDB().catch(err => {
                            console.error('保存数据到 IndexedDB 失败:', err);
                            this.$message.error('保存缓存失败: ' + err.message);
                        });
                    },
                    deep: true
                },
                showAll(newVal) {
                    if (newVal) {
                        this.currentPage = 1;
                    }
                }
            },
            mounted() {
                this.initDB().then(() => {
                    this.loadFromIndexedDB(); // 在数据库初始化后加载数据
                }).catch(err => {
                    console.error('初始化数据库失败:', err);
                    this.$message.error('初始化数据库失败: ' + err.message);
                });
            },
            methods: {
                // --- IndexedDB 相关方法 ---
                initDB() {
                    return new Promise((resolve, reject) => {
                        const request = indexedDB.open(this.dbName, this.dbVersion);
                        request.onerror = (event) => {
                            console.error('IndexedDB 打开失败:', event.target.error);
                            reject(new Error('IndexedDB 打开失败: ' + event.target.error));
                        };
                        request.onsuccess = (event) => {
                            this.db = event.target.result;
                            console.log('IndexedDB 连接成功');
                            resolve(this.db);
                        };
                        request.onupgradeneeded = (event) => {
                            const db = event.target.result;
                            if (!db.objectStoreNames.contains('fbaTrackingStore')) {
                                const objectStore = db.createObjectStore('fbaTrackingStore', { keyPath: 'id' });
                                console.log('创建对象仓库: fbaTrackingStore');
                            }
                        };
                    });
                },
                saveToIndexedDB() {
                    if (!this.db) {
                        console.warn('IndexedDB 未初始化，无法保存数据');
                        return Promise.resolve();
                    }
                    return new Promise((resolve, reject) => {
                        const transaction = this.db.transaction(['fbaTrackingStore'], 'readwrite');
                        const objectStore = transaction.objectStore('fbaTrackingStore');

                        // 准备要保存的数据
                        const dataToSave = {
                            id: 'mainData', // 使用固定ID存储主数据
                            queryResults: this.queryResults,
                            lefengToken: this.lefengToken,
                            lefengTokenExpiry: this.lefengTokenExpiry,
                            dslToken: this.dslToken,
                            dslTokenExpiry: this.dslTokenExpiry
                        };
                        const request = objectStore.put(dataToSave);
                        request.onsuccess = () => {
                            console.log('数据已保存到 IndexedDB');
                            resolve();
                        };
                        request.onerror = (event) => {
                            console.error('保存数据到 IndexedDB 失败:', event.target.error);
                            reject(new Error('保存数据失败: ' + event.target.error));
                        };
                    });
                },
                loadFromIndexedDB() {
                    if (!this.db) {
                        console.warn('IndexedDB 未初始化，无法加载数据');
                        return Promise.resolve();
                    }
                    return new Promise((resolve, reject) => {
                        const transaction = this.db.transaction(['fbaTrackingStore'], 'readonly');
                        const objectStore = transaction.objectStore('fbaTrackingStore');
                        const request = objectStore.get('mainData');
                        request.onsuccess = (event) => {
                            const data = event.target.result;
                            if (data) {
                                try {
                                    this.queryResults = data.queryResults || [];
                                    // 加载认证信息
                                    this.lefengToken = data.lefengToken || null;
                                    this.lefengTokenExpiry = data.lefengTokenExpiry || null;
                                    this.dslToken = data.dslToken || null;
                                    this.dslTokenExpiry = data.dslTokenExpiry || null;
                                    this.$nextTick(() => {
                                        this.calculateColumnWidths();
                                    });
                                    resolve();
                                } catch (e) {
                                    console.error('解析 IndexedDB 数据失败:', e);
                                    this.$message.error('IndexedDB 缓存数据损坏');
                                    reject(e);
                                }
                            } else {
                                console.log('IndexedDB 中没有找到缓存数据');
                                resolve();
                            }
                        };
                        request.onerror = (event) => {
                            console.error('从 IndexedDB 加载数据失败:', event.target.error);
                            this.$message.error('加载缓存数据失败: ' + event.target.error);
                            reject(new Error('加载缓存数据失败: ' + event.target.error));
                        };
                    });
                },
                clearIndexedDB() {
                    if (!this.db) {
                        console.warn('IndexedDB 未初始化，无法清空数据');
                        return Promise.resolve();
                    }
                    return new Promise((resolve, reject) => {
                        const transaction = this.db.transaction(['fbaTrackingStore'], 'readwrite');
                        const objectStore = transaction.objectStore('fbaTrackingStore');
                        const request = objectStore.clear(); // 清空整个对象仓库
                        request.onsuccess = () => {
                            console.log('IndexedDB 数据已清空');
                            resolve();
                        };
                        request.onerror = (event) => {
                            console.error('清空 IndexedDB 数据失败:', event.target.error);
                            reject(new Error('清空缓存数据失败: ' + event.target.error));
                        };
                    });
                },
                // --- 结束 IndexedDB 相关方法 ---

                // 解析输入的FBA号
                parseInputFbas(input) {
                    if (!input.trim()) return [];
                    // 使用换行符、逗号、空格作为分隔符
                    return input.split(/[\n, ]+/).map(fba => fba.trim()).filter(fba => fba.length > 0);
                },

                // 开始查询
                async startQuery() {
                    const fbaList = this.parseInputFbas(this.inputFbas);
                    if (fbaList.length === 0) {
                        this.$message.warning('请输入有效的FBA货件号');
                        return;
                    }

                    this.isQuerying = true;
                    const loading = this.$loading({
                        lock: true,
                        text: '正在查询物流信息...',
                        spinner: 'el-icon-loading',
                        background: 'rgba(0, 0, 0, 0.7)'
                    });

                    try {
                        for (const fbaNumber of fbaList) {
                            // 检查是否已存在，如果存在则先移除（用于更新）
                            const existingIndex = this.queryResults.findIndex(r => r.fbaNumber === fbaNumber);
                            if (existingIndex !== -1) {
                                this.queryResults.splice(existingIndex, 1);
                            }

                            const result = await this.querySingleFba(fbaNumber);
                            if (result) {
                                this.queryResults.unshift(result); // 添加到开头
                            } else {
                                // 如果所有货代都查询失败，添加一个失败记录
                                this.queryResults.unshift({
                                    fbaNumber: fbaNumber,
                                    carrier: '未知',
                                    shippingTime: '',
                                    departureTime: '',
                                    deliveryTime: '',
                                    duration: '查询失败',
                                    status: '未知',
                                    logisticsInfo: null
                                });
                            }
                        }
                        this.$message.success('查询完成');
                    } catch (error) {
                        console.error('查询过程中出错:', error);
                        this.$message.error('查询过程中发生错误: ' + error.message);
                    } finally {
                        loading.close();
                        this.isQuerying = false;
                    }
                },

                // 查询单个FBA号
                async querySingleFba(fbaNumber) {
                    // 遍历所有货代进行查询
                    for (const carrierName in this.carrierMap) {
                        try {
                            const config = this.carrierMap[carrierName];
                            let apiResponseData = null;

                            if (carrierName === "乐丰") {
                                const realOrderNumber = await this.getLefengOrderNumber(fbaNumber);
                                const token = await this.getLefengToken();
                                const requestData = { "orderNumber": realOrderNumber };
                                const response = await fetch(config.trackingUrl, {
                                    method: "POST",
                                    headers: {
                                        "Authorization": `Bearer ${token}`,
                                        "Content-Type": "application/json"
                                    },
                                    body: JSON.stringify(requestData)
                                });
                                if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                                const data = await response.json();
                                if (data.success) {
                                    const orderInfo = data.data.getOrderInfo || {};
                                    const statusInfos = data.data.statusInfos || [];
                                    const shipment = {
                                        client_reference: fbaNumber,
                                        ext_number: realOrderNumber,
                                        status: orderInfo.statusString || "未知状态",
                                        country: orderInfo.country || "",
                                        postcode: "",
                                        traces: statusInfos
                                            .filter(info => info.jobDateString)
                                            .map(info => ({ time: info.jobDateString, info: info.statusString }))
                                            .reverse()
                                    };
                                    apiResponseData = { shipment: shipment };
                                } else {
                                    throw new Error(data.message || '未知错误');
                                }
                            } else if (carrierName === "大森林") {
                                const orderNo = await this.getDslOrderNo(fbaNumber);
                                const token = await this.getDslToken();
                                const url = `${config.trackingUrl}?orderNo=${encodeURIComponent(orderNo)}`;
                                const response = await fetch(url, {
                                    method: "GET",
                                    headers: { "X-Access-Token": token, "Content-Type": "application/json" }
                                });
                                if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                                const data = await response.json();
                                if (data.success) {
                                    const list = data.result.list || [];
                                    const shipment = {
                                        client_reference: fbaNumber,
                                        ext_number: orderNo,
                                        status: list.length > 0 ? list[0].content : "未知状态",
                                        country: "",
                                        postcode: "",
                                        traces: list.map(item => ({ time: item.createTime, info: item.content }))
                                    };
                                    apiResponseData = { shipment: shipment };
                                } else {
                                    throw new Error(data.message || '未知错误');
                                }
                            } else if (carrierName === "南鹏") {
                                const requestData = { "order_numbers": [fbaNumber] };
                                const response = await fetch(config.url, {
                                    method: "POST",
                                    headers: { "Content-Type": "application/json" },
                                    body: JSON.stringify(requestData)
                                });
                                if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                                const data = await response.json();
                                if (data && data.length > 0) {
                                    const shipment = {
                                        client_reference: fbaNumber,
                                        ext_number: fbaNumber,
                                        status: this.getStatusText("delivered"),
                                        country: data[0].pod,
                                        postcode: "",
                                        traces: data[0].details ? data[0].details.map(trace => ({ time: trace.zztm, info: trace.guiji })) : []
                                    };
                                    apiResponseData = { shipment: shipment };
                                } else {
                                    throw new Error('未收到有效数据');
                                }
                            } else {
                                // GET请求 (皓鹏、凯鑫、欧地达、百易)
                                const url = `${config.url}?${config.params}${encodeURIComponent(fbaNumber)}`;
                                const response = await fetch(url);
                                if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                                const data = await response.json();
                                if (data.status === 1) {
                                    apiResponseData = data.data;
                                } else {
                                     // 如果返回错误，继续尝试下一个货代
                                     console.warn(`货代 ${carrierName} 查询 ${fbaNumber} 失败:`, data.info || '未知错误');
                                     continue; // 跳过当前货代，继续循环
                                }
                            }

                            // 如果成功获取到数据，则处理并返回
                            if (apiResponseData) {
                                const result = {
                                    fbaNumber: fbaNumber,
                                    carrier: carrierName,
                                    shippingTime: '',
                                    departureTime: '',
                                    deliveryTime: '',
                                    duration: '',
                                    status: '',
                                    logisticsInfo: apiResponseData
                                };
                                this.updateShippingTimesInResult(result, carrierName, apiResponseData.shipment.traces);
                                return result;
                            }
                        } catch (error) {
                            console.error(`查询货代 ${carrierName} 时出错 (${fbaNumber}):`, error);
                            // 不抛出错误，继续尝试下一个货代
                        }
                    }
                    // 如果所有货代都尝试完仍未成功，返回 null
                    return null;
                },

                // 根据轨迹更新时间字段 (修改为更新 result 对象)
                updateShippingTimesInResult(result, carrier, traces) {
                    if (!traces || traces.length === 0) return;

                    let shippingTime = null;
                    let departureTime = null;
                    let deliveryTime = null;
                    let statusStr = null;

                    const shippingKeywords = ['发货', '已发货', '已入仓', '已收货','已入库'];
                    const departureKeywords = ['开船', '发船', '已发船', '已开航', '离港'];
                    const deliveryKeywords = ['签收', '已签收','POD','货物已提取上网'];

                    traces.forEach(trace => {
                        const timeStr = trace.time;
                        const info = trace.info;
                        if (!timeStr || !info) return;

                        let traceDate = null;
                        const timeParts = timeStr.split(' ')[0].split('-');
                        if (timeParts.length === 3) {
                            traceDate = new Date(`${timeParts[0]}-${timeParts[1]}-${timeParts[2]}`);
                            if (isNaN(traceDate.getTime())) {
                                traceDate = null;
                            }
                        }
                        if (!traceDate) return;

                        if (shippingKeywords.some(keyword => info.includes(keyword))) {
                            if (!shippingTime || traceDate > shippingTime) {
                                shippingTime = traceDate;
                            }
                        }
                        if (departureKeywords.some(keyword => info.includes(keyword))) {
                            if (!departureTime || traceDate > departureTime) {
                                departureTime = traceDate;
                            }
                        }
                        if (deliveryKeywords.some(keyword => info.includes(keyword))) {
                            if (!deliveryTime || traceDate > deliveryTime) {
                                deliveryTime = traceDate;
                            }
                        }
                        if (shippingKeywords.some(keyword => info.includes(keyword))) {
                            if (!statusStr) {
                                statusStr = '已发货';
                            }
                        }
                        if (departureKeywords.some(keyword => info.includes(keyword))) {
                            if (!statusStr) {
                                statusStr = '已发船/车';
                            }
                        }
                        if (deliveryKeywords.some(keyword => info.includes(keyword))) {
                            if (!statusStr) {
                                statusStr = '已签收';
                            }
                        }
                    });

                    // 更新 result 对象
                    result.shippingTime = shippingTime ? this.formatDate(shippingTime) : '';
                    result.departureTime = departureTime ? this.formatDate(departureTime) : '';
                    result.deliveryTime = deliveryTime ? this.formatDate(deliveryTime) : '';
                    result.status = statusStr || '未知';

                    if (departureTime && deliveryTime) {
                        const timeDiff = deliveryTime.getTime() - departureTime.getTime();
                        const daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
                        result.duration = daysDiff;
                    } else if (traces.length > 0) {
                        let time = '';
                        if (carrier == '乐丰') {
                            const [datePart] = traces[0].time.split(' ');
                            const [year, month, day] = datePart.split('-');
                            const monthDay = `${month}-${day}`;
                            time = monthDay;
                        }
                        result.duration = time + traces[0].info;
                    } else {
                        result.duration = '';
                    }
                },


                // --- 以下方法大部分复用或简化自原代码 ---

                // 获取乐丰认证信息 (保持不变)
                async getLefengToken() {
                    if (this.lefengToken && this.lefengTokenExpiry && Date.now() < this.lefengTokenExpiry) {
                        return this.lefengToken;
                    }
                    try {
                        const config = this.carrierMap["乐丰"];
                        const response = await fetch(config.loginUrl, {
                            method: "POST",
                            headers: { "Content-Type": "application/json" },
                            body: JSON.stringify(config.loginData)
                        });
                        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                        const data = await response.json();
                        if (data.status && data.data && data.data.token) {
                            this.lefengToken = data.data.token;
                            this.lefengTokenExpiry = Date.now() + 60 * 60 * 1000;
                            this.saveToIndexedDB().catch(err => console.error('保存乐丰 Token 失败:', err));
                            return this.lefengToken;
                        } else {
                            throw new Error('获取乐丰认证信息失败: ' + (data.message || '未知错误'));
                        }
                    } catch (error) {
                        console.error('获取乐丰认证信息错误:', error);
                        throw new Error('获取乐丰认证信息失败: ' + error.message);
                    }
                },

                // 获取大森林认证信息 (保持不变)
                async getDslToken() {
                    if (this.dslToken && this.dslTokenExpiry && Date.now() < this.dslTokenExpiry) {
                        return this.dslToken;
                    }
                    try {
                        const config = this.carrierMap["大森林"];
                        const response = await fetch(config.loginUrl, {
                            method: "POST",
                            headers: { "Content-Type": "application/json" },
                            body: JSON.stringify(config.loginData)
                        });
                        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                        const data = await response.json();
                        if (data.success && data.result && data.result.token) {
                            this.dslToken = data.result.token;
                            this.dslTokenExpiry = Date.now() + 60 * 60 * 1000;
                            this.saveToIndexedDB().catch(err => console.error('保存大森林 Token 失败:', err));
                            return this.dslToken;
                        } else {
                            throw new Error('获取大森林认证信息失败: ' + (data.message || '未知错误'));
                        }
                    } catch (error) {
                        console.error('获取大森林认证信息错误:', error);
                        throw new Error('获取大森林认证信息失败: ' + error.message);
                    }
                },

                // 获取乐丰真正的订单号 (保持不变)
                async getLefengOrderNumber(fbaNumber) {
                    try {
                        const token = await this.getLefengToken();
                        const config = this.carrierMap["乐丰"];
                        const requestData = {
                            "page": 1,
                            "rows": 30,
                            "sort": "create_time",
                            "order": "desc",
                            "wheres": `[{"name":"orderNumber","value":"${fbaNumber}","displayType":"text"},{"name":"orderTypeSelect","value":"2","displayType":"text"}]`,
                            "orderStatus": "-1"
                        };
                        const response = await fetch(config.getPageDataUrl, {
                            method: "POST",
                            headers: {
                                "Authorization": `Bearer ${token}`,
                                "Content-Type": "application/json"
                            },
                            body: JSON.stringify(requestData)
                        });
                        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                        const data = await response.json();
                        if (data.status === 0 && data.rows && data.rows.length > 0) {
                            return data.rows[0].orderNumber;
                        } else {
                            throw new Error('未找到对应的乐丰订单号');
                        }
                    } catch (error) {
                        console.error('获取乐丰订单号错误:', error);
                        throw new Error('获取乐丰订单号失败: ' + error.message);
                    }
                },

                // 获取大森林的orderNo (保持不变)
                async getDslOrderNo(shipmentId) {
                    try {
                        const token = await this.getDslToken();
                        const config = this.carrierMap["大森林"];
                        const url = `${config.getOrderPageUrl}?isErrorEmail=0&cusId=${config.cusId}&pageNo=1&pageSize=10&column=createTime&order=desc&shipmentId=${encodeURIComponent(shipmentId)}`;
                        const response = await fetch(url, {
                            method: "GET",
                            headers: {
                                "X-Access-Token": token,
                                "Content-Type": "application/json"
                            }
                        });
                        if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                        const data = await response.json();
                        if (data.success && data.result && data.result.records && data.result.records.length > 0) {
                            return data.result.records[0].orderNo;
                        } else {
                            throw new Error('未找到对应的大森林订单号');
                        }
                    } catch (error) {
                        console.error('获取大森林订单号错误:', error);
                        throw new Error('获取大森林订单号失败: ' + error.message);
                    }
                },

                // 获取状态文本 (保持不变)
                getStatusText(status) {
                    return this.statusMap[status] || status;
                },

                // 格式化日期 (保持不变)
                formatDate(date) {
                    if (!date || !(date instanceof Date) || isNaN(date)) return '';
                    const year = date.getFullYear();
                    const month = String(date.getMonth() + 1).padStart(2, '0');
                    const day = String(date.getDate()).padStart(2, '0');
                    const hours = String(date.getHours()).padStart(2, '0');
                    const minutes = String(date.getMinutes()).padStart(2, '0');
                    const seconds = String(date.getSeconds()).padStart(2, '0');
                    if (hours === '00' && minutes === '00' && seconds === '00') {
                        return `${year}-${month}-${day}`;
                    } else {
                        return `${year}-${month}-${day} `;
                    }
                },

                // 分页控制 (保持不变)
                previousPage() {
                    if (this.currentPage > 1) {
                        this.currentPage--;
                    }
                },
                nextPage() {
                    if (this.currentPage < this.totalPages) {
                        this.currentPage++;
                    }
                },

                // 排序 (简化)
                handleSortChange({ column, prop, order }) {
                    if (!order) {
                        this.currentSortColumn = null;
                        this.queryResults.sort((a, b) => a.fbaNumber.localeCompare(b.fbaNumber)); // 默认按FBA号排序
                        return;
                    }
                    this.currentSortColumn = prop;
                    this.sortDirection = order === 'ascending' ? 'asc' : 'desc';

                    this.queryResults.sort((a, b) => {
                        let valA = a[prop];
                        let valB = b[prop];

                        // 处理数字排序
                        if (prop === 'duration' && !isNaN(valA) && !isNaN(valB)) {
                             return this.sortDirection === 'asc' ?
                                parseFloat(valA) - parseFloat(valB) :
                                parseFloat(valB) - parseFloat(valA);
                        }

                        // 处理日期排序 (假设是字符串格式)
                        if (prop.includes('Time') && valA && valB) {
                            const dateA = new Date(valA);
                            const dateB = new Date(valB);
                            if (!isNaN(dateA) && !isNaN(dateB)) {
                                 return this.sortDirection === 'asc' ?
                                    dateA - dateB :
                                    dateB - dateA;
                            }
                        }

                        // 默认字符串排序
                        valA = valA || '';
                        valB = valB || '';
                        const comp = String(valA).localeCompare(String(valB));
                        return this.sortDirection === 'asc' ? comp : -comp;
                    });
                },

                // 列宽计算 (简化)
                calculateColumnWidths() {
                    const widths = {};
                    const columnProps = ['fbaNumber', 'carrier', 'shippingTime', 'departureTime', 'deliveryTime', 'duration', 'status'];
                    const sampleRows = this.queryResults.slice(0, 20);

                    columnProps.forEach(prop => {
                        let maxWidth = this.getStringWidth(prop); // Header width
                        sampleRows.forEach(row => {
                            const cellValue = String(row[prop] || '');
                            const cellWidth = this.getStringWidth(cellValue);
                            if (cellWidth > maxWidth) {
                                maxWidth = cellWidth;
                            }
                        });
                        widths[prop] = Math.max(80, Math.min(maxWidth + 40, 350));
                    });
                    this.columnWidths = widths;
                },
                getStringWidth(str) {
                    if (!str) return 60;
                    let width = 0;
                    for (let i = 0; i < str.length; i++) {
                        const char = str.charAt(i);
                        if (char.match(/[\u4e00-\u9fa5]/)) {
                            width += 17;
                        } else {
                            width += 8;
                        }
                    }
                    return Math.max(width, 60);
                },
                getColumnWidth(prop) {
                    return this.columnWidths[prop] || 120;
                },
                autoAdjustColumnWidth() {
                    this.calculateColumnWidths();
                    this.$message.success('列宽已自动调整');
                },

                // 导出Excel (修改)
                exportToExcel() {
                    if (this.queryResults.length === 0) {
                        this.$message.warning('没有可导出的数据');
                        return;
                    }
                    const headerRow = ['FBA货件号', '货代', '发货时间', '开船时间', '签收时间', '开船-签收时长', '状态'];
                    const dataRows = this.queryResults.map(row => [
                        row.fbaNumber,
                        row.carrier,
                        row.shippingTime,
                        row.departureTime,
                        row.deliveryTime,
                        row.duration,
                        row.status
                    ]);
                    const worksheetData = [headerRow, ...dataRows];
                    const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);
                    const workbook = XLSX.utils.book_new();
                    XLSX.utils.book_append_sheet(workbook, worksheet, 'FBA物流追踪结果');
                    const fileName = `FBA物流追踪结果_${new Date().toLocaleDateString().replace(/\//g, '-')}.xlsx`;
                    XLSX.writeFile(workbook, fileName);
                },

                // 清空历史数据
                clearHistory() {
                    this.$confirm('确定要清空所有历史查询记录吗？此操作不可恢复。', '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        this.queryResults = [];
                        this.clearIndexedDB().catch(err => {
                            console.error('清空 IndexedDB 失败:', err);
                            this.$message.error('清空缓存失败: ' + err.message);
                        });
                        this.$message.success('历史记录已清空');
                    }).catch(() => {
                        // 用户取消
                    });
                },

                // 物流信息查询 (用于表格中的单行查询按钮)
                async fetchLogisticsInfo(rowData) {
                    if (!rowData.fbaNumber) {
                        this.$message.warning('FBA货件号为空');
                        return;
                    }
                    let loadingInstance = null;
                    try {
                        loadingInstance = this.$loading({
                            lock: true,
                            text: '正在查询物流信息...',
                            spinner: 'el-icon-loading',
                            background: 'rgba(0, 0, 0, 0.7)'
                        });

                        const result = await this.querySingleFba(rowData.fbaNumber);
                        if (result) {
                            // 找到对应行并更新
                            const index = this.queryResults.findIndex(r => r.fbaNumber === rowData.fbaNumber);
                            if (index !== -1) {
                                this.$set(this.queryResults, index, result);
                            }
                            this.$message.success('物流信息查询成功');
                        } else {
                            this.$message.error('物流信息查询失败');
                        }
                    } catch (error) {
                        console.error('查询物流信息错误:', error);
                        this.$message.error('查询物流信息失败: ' + error.message);
                    } finally {
                        if (loadingInstance) loadingInstance.close();
                    }
                },

                // 显示物流详情弹窗
                showLogisticsDetail(row) {
                    this.currentLogisticsInfo = row.logisticsInfo;
                    this.logisticsDialogVisible = true;
                },
            }
        });
    </script>
</body>
</html>